#!/usr/bin/env python
# coding: utf-8

# # 抽象语法树AST
# 
# 本节目标：扩展文法，能够处理一元运算符（+-）
# 
# 实现方法：
# 添加一个新的AST节点类UnaryOp，构造一元运算符的节点
# 扩展语法分析器，以便能生成带有一元运算符节点的AST
# 扩展解释器，病添加一个新的visit_UnaryOp()方法来解释一元运算符

# In[1]:


INTEGER, PLUS, MINUS, MUL, DIV, LPAREN, RPAREN, EOF = 'INTERGER', 'PLUS', 'MINUS', 'MUL', 'DIV', 'LPAREN', 'RPAREN', 'EOF'


# In[2]:


class Token:
    """记号类
    """
    def __init__(self, value_type, value):
        self.value_type = value_type # 记号的类型
        self.value = value # 记号的值
    
    def __str__(self):
        """重写查看记号内容的方法"""
        return f"Token({self.value_type}, {self.value})"
    
    def __repr__(self):
        return self.__str__()


# In[3]:


class Lexer:
    """解释器类
    """
    def __init__(self, text):
        self.text = text
        self.position = 0
        self.current_char = self.text[self.position]
    
    def error(self):
        raise Exception('警告：错误的输入内容！')
    
    def advance(self):
        """定义获取下一个字符的方法
        """
        self.position += 1
        if self.position >= len(self.text):
            self.current_char = None
        else:
            self.current_char = self.text[self.position]
    
    def skip_whitespace(self):
        """定义跳过空格的方法
        """
        while self.current_char is not None and self.current_char.isspace():
            self.advance()
    
    def long_integer(self):
        result = ''
        while self.current_char is not None and self.current_char.isdigit():
            result += self.current_char
            self.advance()
        return int(result)
            
    def get_next_token(self):
        while self.current_char is not None:
            if self.current_char.isspace():
                self.skip_whitespace()
                continue
            if self.current_char.isdigit():
                token = Token(INTEGER, self.long_integer())
                return token
            if self.current_char == '+':
                token = Token(PLUS, self.current_char)
                self.advance()
                return token
            if self.current_char == '-':
                token = Token(MINUS, self.current_char)
                self.advance()
                return token
            if self.current_char == '*':
                token = Token(MUL, self.current_char)
                self.advance()
                return token
            if self.current_char == '/':
                token = Token(DIV, self.current_char)
                self.advance()
                return token
            if self.current_char == '(':
                token = Token(LPAREN, self.current_char)
                self.advance()
                return token
            if self.current_char == ')':
                token = Token(RPAREN, self.current_char)
                self.advance()
                return token
            self.error()
        return Token(EOF, None)


# # 支持括号嵌套的文法
# 
# 扩展新的文法：关键点在于factor可以自包含，增加一个正负号前缀
# factor是基本单位，表示带符号整数或者带括号的表达式；
# term表示乘除法；
# expr表示加减法
# 文法从上到下，表达优先级从低到高
# ```
# expr: term((PLUS|MINUS)term)*
# term: factor((MUL|DIV)factor)*
# factor: (PLUS|MINUS)factor|INTEGER|LPAREN expr RPAREN
# ```

# In[4]:


class AST:
    """抽象语法树基类
    """
    pass


# In[5]:


class Num(AST):
    """数字节点
    """
    def __init__(self, token):
        self.token = token
        self.value = token.value


# In[6]:


class BinOp(AST):
    """二元运算符节点
    """
    def __init__(self, left, op, right):
        self.left = left
        self.token = self.op = op
        self.right = right


# In[7]:


class UnaryOp(AST):
    """一元运算符节点
    """
    def __init__(self, op, expr):
        self.token = self.op = op
        self.expr = expr


# In[8]:


class Parser:
    """语法解析器
    """
    def __init__(self, lexer):
        self.lexer = lexer
        self.current_token = lexer.get_next_token()
    
    def eat(self, token_type):
        print(f"eating {self.current_token}")
        if self.current_token.value_type == token_type:
            if token_type != EOF:
                self.current_token = self.lexer.get_next_token()
        else:
            self.error()
    
    def factor(self):
        token = self.current_token
        if token.value_type in (PLUS, MINUS):
            self.eat(token.value_type)
            return UnaryOp(token, self.factor())
        if token.value_type == INTEGER:
            self.eat(INTEGER)
            return Num(token)
        elif token.value_type == LPAREN:
            self.eat(LPAREN)
            node = self.expr()
            self.eat(RPAREN)
            return node
    
    def term(self):
        """乘除法
        """
        node = self.factor()
        
        while self.current_token.value_type in (MUL, DIV):
            token = self.current_token
            if token.value_type == MUL:
                self.eat(MUL)
            if self.current_token.value_type == DIV:
                self.eat(DIV)
            node = BinOp(left=node, op=token, right=self.factor())
        return node

    
    def expr(self):
        """加减法
        """
        node = self.term()
        
        while self.current_token.value_type in (PLUS, MINUS):
            token = self.current_token
            if token.value_type == PLUS:
                self.eat(PLUS)
            if token.value_type == MINUS:
                self.eat(MINUS)
            node = BinOp(left=node, op=token, right=self.term())
        return node
    
    def parse(self):
        return self.expr()


# # 应用AST结构
# 需要使用到访问者模式，后续遍历AST

# In[9]:


class NodeVisitor:
    """定义节点访问器类
    """
    def visit(self, node):
        method_name = 'visit_' + type(node).__name__
        print(f"visitor = {method_name}")
        visitor = getattr(self, method_name, self.generic_visitor)
        return visitor(node)
    
    def generic_visitor(self, node):
        raise Exception(f"未找到visit_{type(node).__name__}()方法")
        


# In[10]:


class Interpreter(NodeVisitor):
    """解释器类
    """
    def __init__(self, parser):
        self.parser = parser
    
    def visit_BinOp(self, node):
        if node.op.value_type == PLUS:
            return self.visit(node.left) + self.visit(node.right)
        elif node.op.value_type == MINUS:
            return self.visit(node.left) - self.visit(node.right)
        elif node.op.value_type == MUL:
            return self.visit(node.left) * self.visit(node.right)
        elif node.op.value_type == DIV:
            return self.visit(node.left) / self.visit(node.right)
    
    def visit_Num(self, node):
        return node.value
    
    def visit_UnaryOp(self, node):
        if node.op.value_type == PLUS:
            return +self.visit(node.expr)
        elif node.op.value_type == MINUS:
            return -self.visit(node.expr)
    
    def interpret(self):
        tree = self.parser.parse()
        return self.visit(tree)


# In[11]:


def main():
    while True:
        text = input("输入算式：")
        if text == 'q':
            break
        lexer = Lexer(text)
        parser = Parser(lexer)
        interpreter = Interpreter(parser)
        result = interpreter.interpret()
        print(text, "=", result)


# In[ ]:


main()

